Skip to content

fix(#160): correct AS↔DC token/introspection wire format to ESPI standard + pin contract (#150)#161

Merged
dfcoffin merged 1 commit into
mainfrom
feature/issue-150-as-dc-contract-fixtures
Jun 4, 2026
Merged

fix(#160): correct AS↔DC token/introspection wire format to ESPI standard + pin contract (#150)#161
dfcoffin merged 1 commit into
mainfrom
feature/issue-150-as-dc-contract-fixtures

Conversation

@dfcoffin

@dfcoffin dfcoffin commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

Closes #160. Refs #150.

Problem (live bug, surfaced while scoping the #150 contract)

The C4 token-response/introspection augmentation diverged from the ESPI 4.0 standard (NAESB REQ.21 — its own example shows "resourceURI":".../resource/Batch/Subscription/{id}", "scope":"FB=...;...", only the three *URI fields, customerResourceURI may be "").

The producer emitted /resource/Subscription/{id}missing /Batch/ — which DC's own ResourceValidationFilter already rejected (it expects /resource/Batch/Subscription). So a real customer token failed DC's resource validation. The AS also emitted three non-standard *_id claims (one mislabeled customer_subscription_id that was actually the retail-customer id).

Root-cause fix — one canonical builder/parser (no string patch)

The drift existed because producer and consumers each encoded the URL format independently.

  • EspiBatchUri (openespi-common) — the single home for the ESPI Batch URL format: builds and parses Batch/{Subscription|Bulk|RetailCustomer}/{id} and Authorization/{id}. A test asserts the legacy /Subscription/{id} form does not parse as a Batch subscription.
  • Producer (SubscriptionProvisioningServiceImpl) builds all three canonical URIs via EspiBatchUriresourceURIBatch/Subscription, authorizationURIAuthorization, customerResourceURIBatch/RetailCustomer/{rc} (built from the retail-customer id DC holds, not the round-tripped handoff value).
  • *_id claims dropped from the AS customizer + token-response handler; consumers parse ids from the canonical URIs. (Verified openespi-thirdparty doesn't read them.)

Contract pinned (#150) — lightweight, no Spring Cloud

These are standalone EC2 services, not a microservice mesh — so a shared-fixture consumer-driven contract, not Spring Cloud Contract:

  • contracts/ holds the two ESPI-standard fixtures (Subscription + Bulk) as the single source of truth + a README.
  • DC consumer test binds DC's parsing (EspiBatchUri + EspiScope) to the fixtures (asserts *_id absent, FB-grammar-only scope). Producer test asserts all three emitted URIs are canonical — so the contract is verified on both sides.

Verification

Follow-ups (noted in the commit)

  • Remove the now-vestigial customer_resource_uri from the back-channel request/handoff (structural cleanup).
  • Empirically verify /oauth2/introspect surfaces the URI claims (note: DC enforces from its own stored Authorization.resourceURI, now the Batch form — the live path is fixed).

🤖 Generated with Claude Code

…standard + pin it (#150)

The C4 token-response/introspection augmentation diverged from the ESPI 4.0 standard
(NAESB REQ.21, whose own example shows `resourceURI=.../resource/Batch/Subscription/{id}`,
`scope="FB=...;..."`, only the three *URI fields, `customerResourceURI` may be ""). The
producer emitted `/resource/Subscription/{id}` (missing `/Batch/`) — which DC's own
ResourceValidationFilter already rejected (it expects `/resource/Batch/Subscription`), so a
real customer token failed DC's resource validation. The AS also emitted three non-standard
`*_id` claims (one mislabeled). Surfaced while scoping the #150 contract.

Root-cause fix (not a string patch): a single canonical builder/parser so producer and
consumers can't drift again.
- openespi-common EspiBatchUri: the one home for the ESPI Batch URL format —
  builds and parses `Batch/{Subscription|Bulk|RetailCustomer}/{id}` and `Authorization/{id}`.
  A test asserts the legacy `/Subscription/{id}` form does NOT parse as a Batch subscription.
- SubscriptionProvisioningServiceImpl (producer) builds all three canonical URIs via
  EspiBatchUri: resourceURI -> Batch/Subscription, authorizationURI -> Authorization, and
  customerResourceURI -> Batch/RetailCustomer/{rc} (built from the retail-customer id DC
  holds, rather than trusting the round-tripped handoff value).
- Dropped the three non-standard `*_id` claims from the AS token customizer + the
  token-response handler's claim set; consumers parse ids from the canonical URIs instead.
  (Verified openespi-thirdparty does not read the `*_id` fields.)

Contract pin (#150), lightweight — no Spring Cloud (these are standalone EC2 services):
- contracts/ holds the two ESPI-standard fixtures (Subscription + Bulk) as the shared
  single source of truth, with a README explaining the producer/consumer binding.
- DC consumer test (IntrospectionWireContractTest) binds DC's parsing (EspiBatchUri +
  EspiScope) to the fixtures and asserts the `*_id` fields are absent and the scope is
  FB-grammar only. The producer test asserts all three emitted URIs are the canonical forms,
  so the contract is verified on both sides.

Tests updated: GrantBackchannelTokenCustomizerTest, the #148 orchestration test (asserts the
`*_id` fields now do-not-exist), SubscriptionProvisioningServiceImplTest. All green:
common 18/18 + DC contract/controller + AS testcontainers-it 20/20.

Follow-ups (noted): remove the now-vestigial `customer_resource_uri` from the back-channel
request/handoff (structural cleanup); empirically verify `/oauth2/introspect` surfaces the
URI claims (DC enforces from its own stored Authorization.resourceURI, now the Batch form).

Refs #150. Closes #160.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dfcoffin dfcoffin merged commit 2eeb5d2 into main Jun 4, 2026
4 checks passed
@dfcoffin dfcoffin deleted the feature/issue-150-as-dc-contract-fixtures branch June 4, 2026 22:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AS↔DC token/introspection wire format diverges from ESPI standard (Batch URI forms + non-standard *_id claims)

1 participant